﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Physis.Data.Readers {

    public class DataReader {

        /// <summary>
        /// Reads a String value from a database field in a DataRow object. This method ensures that the column exists and is not NULL.
        /// </summary>
        /// <param name="dataRow">Reference to a DataRow object that contains the column and value.</param>
        /// <param name="fieldName">The column to retreive the value from the row.</param>
        /// <returns>Value in field/row or String.Empty if column or value does not exist.</returns>
        public static String StringFromDatabase (System.Data.DataRow dataRow, String fieldName) {

            String value = String.Empty;


            if (dataRow.Table.Columns.Contains (fieldName)) {

                if (!(dataRow[fieldName] is DBNull)) {

                    value = Convert.ToString (dataRow[fieldName]).TrimEnd ();

                }

            }

#if DEBUG_DATAMAPPINGS

            else {

                System.Diagnostics.Debug.Write ("----> DatabaseReader.StringFromDatabase.FieldNotFound [" + fieldName + "]: " + (new System.Diagnostics.StackTrace ().GetFrames ()[1].GetMethod ().Name));

                System.Diagnostics.Debug.Write ("   <-- FROM -->   " + (new System.Diagnostics.StackTrace ().GetFrames ()[2].GetMethod ().Name));

                System.Diagnostics.Debug.Write ("   <-- FROM -->   " + (new System.Diagnostics.StackTrace ().GetFrames ()[3].GetMethod ().Name));

                System.Diagnostics.Debug.WriteLine (String.Empty);

            }

#endif

            return value;

        }

        /// <summary>
        /// Reads a String value from a database field in a DataRow object. This method ensures that the column exists and is not NULL.
        /// In addition, this method allows for a second column to be read from if the first column does not exist in the row OR if it has no value.
        /// </summary>
        /// <param name="dataRow">Reference to a DataRow object that contains the column and value.</param>
        /// <param name="fieldName">The first column to retreive the value from the row.</param>
        /// <param name="fallbackFieldName">The second column to retreive the value from the row if the first column does not exist.</param>
        /// <returns>Value in field/row or String.Empty if column or value does not exist.</returns>
        public static String StringFromDatabase (System.Data.DataRow dataRow, String fieldName, String fallbackFieldName) {

            String value = String.Empty;

            // TRY TO READ FROM FIRST FIELD IF IT EXISTS 

            if (dataRow.Table.Columns.Contains (fieldName)) { value = StringFromDatabase (dataRow, fieldName); }

            // TRY TO READ FROM SECOND FIELD IF FIRST FIELD DOES NOT EXIST OR NO VALUE FOUND

            if (String.IsNullOrWhiteSpace (value)) { value = StringFromDatabase (dataRow, fallbackFieldName); }

            return value;

        }

        /// <summary>
        /// Reads a Date/Time value from a database field in a DataRow object. This method ensures that the column exists and is not NULL.
        /// In addition, it converts the AMISYS CCYYMMDD String format storage into a DateTime data type. Does allow NULLS.
        /// </summary>
        /// <param name="dataRow">Reference to a DataRow object that contains the column and value.</param>
        /// <param name="fieldName">The column to retreive the value from the row.</param>
        /// <returns>Value in field/row or String.Empty if column or value does not exist.</returns>
        public static DateTime? DateTimeFromDatabaseAllowNull (System.Data.DataRow dataRow, String fieldName) {

            DateTime? value = null;

            String databaseValue = String.Empty;

            DateTime convertedDateTime = DateTime.Now;


            if (dataRow.Table.Columns.Contains (fieldName)) {

                if (!(dataRow[fieldName] is DBNull)) {

                    if (dataRow[fieldName] is DateTime) { value = (DateTime)dataRow[fieldName]; }

                    else { // TRY CUSTOM STORAGE FORMAT 

                        // DETERMINE HOW IT IS STORED IN THE DATABASE 

                        if (dataRow[fieldName] is Int32) { // INT32 IN YYYYMMDD FORMAT 

                            databaseValue = ((Int32)dataRow[fieldName]).ToString (); // CONVERT INTERGER TO STRING AND LEFT PAD 

                            databaseValue = databaseValue.Trim ().PadLeft (8, '0');

                            if (databaseValue != "00000000") {

                                if (DateTime.TryParseExact (databaseValue, "yyyyMMdd", System.Globalization.CultureInfo.InvariantCulture, System.Globalization.DateTimeStyles.None, out convertedDateTime)) {

                                    value = convertedDateTime;

                                }

                            }

                        }

                        else {  // STRING FORMAT 
                            
                            databaseValue = Convert.ToString (dataRow[fieldName]);

                            if (DateTime.TryParse (databaseValue, out convertedDateTime)) { 

                                value = convertedDateTime;

                            }
                        
                        }


                    }

                }

            }

#if DEBUG_DATAMAPPINGS

            else {

                System.Diagnostics.Debug.Write ("----> DatabaseReader.DateTimeFromDatabaseAllowNull.FieldNotFound [" + fieldName + "]: " + (new System.Diagnostics.StackTrace ().GetFrames ()[1].GetMethod ().Name));

                System.Diagnostics.Debug.Write ("   <-- FROM -->   " + (new System.Diagnostics.StackTrace ().GetFrames ()[2].GetMethod ().Name));

                System.Diagnostics.Debug.Write ("   <-- FROM -->   " + (new System.Diagnostics.StackTrace ().GetFrames ()[3].GetMethod ().Name));

                System.Diagnostics.Debug.WriteLine (String.Empty);

            }

#endif


            return value;

        }

        /// <summary>
        /// Reads a Date/Time value from a database field in a DataRow object. This method ensures that the column exists and is not NULL.
        /// In addition, it converts the AMISYS CCYYMMDD String format storage into a DateTime data type. Does NOT allow NULLS.
        /// </summary>
        /// <param name="dataRow">Reference to a DataRow object that contains the column and value.</param>
        /// <param name="fieldName">The column to retreive the value from the row.</param>
        /// <returns>Value in field/row or String.Empty if column or value does not exist.</returns>
        public static DateTime DateTimeFromDatabase (System.Data.DataRow dataRow, String fieldName) {

            DateTime? databaseValue = DateTimeFromDatabaseAllowNull (dataRow, fieldName);

            return (databaseValue.HasValue) ? databaseValue.Value : DateTime.Today;

        }

        /// <summary>
        /// Reads a Int32 value from a database field in a DataRow object. This method ensures that the column exists and is not NULL.
        /// </summary>
        /// <param name="dataRow">Reference to a DataRow object that contains the column and value.</param>
        /// <param name="fieldName">The column to retreive the value from the row.</param>
        /// <returns>Value in field/row or 0 if column or value does not exist.</returns>
        public static Int32 Int32FromDatabase (System.Data.DataRow dataRow, String fieldName) {

            Int32 value = 0;


            if (dataRow.Table.Columns.Contains (fieldName)) {

                if (!(dataRow[fieldName] is DBNull)) {

                    if (dataRow[fieldName] is Int32) { value = (Int32)dataRow[fieldName]; }

                    else { Int32.TryParse (Convert.ToString (dataRow[fieldName]), out value); }

                }

            }

#if DEBUG_DATAMAPPINGS

            else {

                System.Diagnostics.Debug.Write ("----> DatabaseReader.Int32FromDatabase.FieldNotFound [" + fieldName + "]: " + (new System.Diagnostics.StackTrace ().GetFrames ()[1].GetMethod ().Name));

                System.Diagnostics.Debug.Write ("   <-- FROM -->   " + (new System.Diagnostics.StackTrace ().GetFrames ()[2].GetMethod ().Name));

                System.Diagnostics.Debug.Write ("   <-- FROM -->   " + (new System.Diagnostics.StackTrace ().GetFrames ()[3].GetMethod ().Name));

                System.Diagnostics.Debug.WriteLine (String.Empty);

            }

#endif


            return value;

        }

        /// <summary>
        /// Reads a Int64 value from a database field in a DataRow object. This method ensures that the column exists and is not NULL.
        /// </summary>
        /// <param name="dataRow">Reference to a DataRow object that contains the column and value.</param>
        /// <param name="fieldName">The column to retreive the value from the row.</param>
        /// <returns>Value in field/row or 0 if column or value does not exist.</returns>
        public static Int64 Int64FromDatabase (System.Data.DataRow dataRow, String fieldName, Int64 defaultValue = 0) {

            Int64 value = defaultValue;


            if (dataRow.Table.Columns.Contains (fieldName)) {

                if (!(dataRow[fieldName] is DBNull)) {

                    if (dataRow[fieldName] is Int64) { value = (Int64)dataRow[fieldName]; }

                    else { Int64.TryParse (Convert.ToString (dataRow[fieldName]), out value); }

                }

            }

#if DEBUG_DATAMAPPINGS

            else {

                System.Diagnostics.Debug.Write ("----> DatabaseReader.Int64FromDatabase.FieldNotFound [" + fieldName + "]: " + (new System.Diagnostics.StackTrace ().GetFrames ()[1].GetMethod ().Name));

                System.Diagnostics.Debug.Write ("   <-- FROM -->   " + (new System.Diagnostics.StackTrace ().GetFrames ()[2].GetMethod ().Name));

                System.Diagnostics.Debug.Write ("   <-- FROM -->   " + (new System.Diagnostics.StackTrace ().GetFrames ()[3].GetMethod ().Name));

                System.Diagnostics.Debug.WriteLine (String.Empty);

            }

#endif



            return value;

        }

        /// <summary>
        /// Reads a Decimal value from a database field in a DataRow object. This method ensures that the column exists and is not NULL.
        /// </summary>
        /// <param name="dataRow">Reference to a DataRow object that contains the column and value.</param>
        /// <param name="fieldName">The column to retreive the value from the row.</param>
        /// <returns>Value in field/row or 0 if column or value does not exist.</returns>
        public static Decimal DecimalFromDatabase (System.Data.DataRow dataRow, String fieldName) {

            Decimal value = 0;


            if (dataRow.Table.Columns.Contains (fieldName)) {

                if (!(dataRow[fieldName] is DBNull)) {

                    if (dataRow[fieldName] is Decimal) { value = (Decimal)dataRow[fieldName]; }

                    else { Decimal.TryParse (Convert.ToString (dataRow[fieldName]), out value); }

                }

            }

#if DEBUG_DATAMAPPINGS

            else {

                System.Diagnostics.Debug.Write ("----> DatabaseReader.DecimalFromDatabase.FieldNotFound [" + fieldName + "]: " + (new System.Diagnostics.StackTrace ().GetFrames ()[1].GetMethod ().Name));

                System.Diagnostics.Debug.Write ("   <-- FROM -->   " + (new System.Diagnostics.StackTrace ().GetFrames ()[2].GetMethod ().Name));

                System.Diagnostics.Debug.Write ("   <-- FROM -->   " + (new System.Diagnostics.StackTrace ().GetFrames ()[3].GetMethod ().Name));

                System.Diagnostics.Debug.WriteLine (String.Empty);

            }

#endif



            return value;

        }

        /// <summary>
        /// Reads a Boolean value from a database field that is a number in a DataRow object. This method ensures that the column exists and is not NULL.
        /// THIS FUNCTION IS A SINGLE READ FUNCTION TO SUPPORT THE STORAGE OF BOOLEAN VALUES IN THE DATABASE 
        /// AS EITHER A BIT (SQL SERVER) OR A NUMERIC FIELD (ORACLE) OR AS A Y/N FLAG 
        /// </summary>
        /// <param name="dataRow">Reference to a DataRow object that contains the column and value.</param>
        /// <param name="fieldName">The column to retreive the value from the row.</param>
        /// <returns>Value in field/row or FALSE if column or value does not exist.</returns>
        public static Boolean BooleanFromDatabase (System.Data.DataRow dataRow, String fieldName) {

            Boolean value = false;


            // THIS FUNCTION IS A SINGLE READ FUNCTION TO SUPPORT THE STORAGE OF BOOLEAN VALUES IN THE DATABASE 

            // AS EITHER A BIT (SQL SERVER) OR A NUMERIC FIELD (ORACLE) OR AS A Y/N FLAG 

            if (dataRow.Table.Columns.Contains (fieldName)) {

                if (!(dataRow[fieldName] is DBNull)) {

                    if (dataRow[fieldName] is Boolean) { value = (Boolean)dataRow[fieldName]; }

                    else {

                        // SEE IF NUMBER IS STORED IN THE FIELD (EITHER AS AN INTEGER OR AS A STRING), ANY POSITIVE NON-ZERO NUMBER IS TRUE

                        // THIS IS MAINLY TO SUPPORT ORACLE STORAGE AS NUMBER (1) - BASICALLY AS NUMBER DATA TYPE IN ORACLE, WHICH ACTUALLY COMES IN AS INT16

                        String valueString = Convert.ToString (dataRow[fieldName]); // FORCE TO STRING VERSION OF DATA VALUE

                        Int32 valueInt32 = 0; // HOLD VALUE NUMERIC INTEGER READ 

                        if (Int32.TryParse (valueString, out valueInt32)) {

                            value = (valueInt32 != 0);

                        }

                        // OTHERWISE, TRY TO LOOK FOR "Y" IN ALPHA NUMERIC FIELD

                        else if (((String)dataRow[fieldName]).PadRight (1).Substring (0, 1).ToUpper () == "Y") { value = true; }

                        // DEFAULT TO FALSE 

                    }

                }

            }

#if DEBUG_DATAMAPPINGS

            else {

                System.Diagnostics.Debug.Write ("----> DatabaseReader.BooleanFromDatabase.FieldNotFound [" + fieldName + "]: " + (new System.Diagnostics.StackTrace ().GetFrames ()[1].GetMethod ().Name));

                System.Diagnostics.Debug.Write ("   <-- FROM -->   " + (new System.Diagnostics.StackTrace ().GetFrames ()[2].GetMethod ().Name));

                System.Diagnostics.Debug.Write ("   <-- FROM -->   " + (new System.Diagnostics.StackTrace ().GetFrames ()[3].GetMethod ().Name));

                System.Diagnostics.Debug.WriteLine (String.Empty);

            }

#endif


            return value;

        }

    }

}
